package com.yxqz.manage_cms.service;

import com.alibaba.fastjson.JSON;
import com.mongodb.client.gridfs.GridFSBucket;
import com.mongodb.client.gridfs.GridFSDownloadStream;
import com.mongodb.client.gridfs.model.GridFSFile;
import com.xuecheng.framework.domain.cms.CmsConfig;
import com.xuecheng.framework.domain.cms.CmsPage;
import com.xuecheng.framework.domain.cms.CmsTemplate;
import com.xuecheng.framework.domain.cms.request.QueryPageRequest;
import com.xuecheng.framework.domain.cms.response.CmsCode;
import com.xuecheng.framework.domain.cms.response.CmsPageResult;
import com.xuecheng.framework.exception.ExceptionCast;
import com.xuecheng.framework.model.response.CommonCode;
import com.xuecheng.framework.model.response.QueryResponseResult;
import com.xuecheng.framework.model.response.QueryResult;
import com.xuecheng.framework.model.response.ResponseResult;
import com.yxqz.manage_cms.config.MongoConfig;
import com.yxqz.manage_cms.config.RabbitmqConfig;
import com.yxqz.manage_cms.dao.CmsConfigRepository;
import com.yxqz.manage_cms.dao.CmsPageRepository;
import com.yxqz.manage_cms.dao.CmsTemplateRepository;
import freemarker.cache.StringTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.bson.types.ObjectId;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.*;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.gridfs.GridFsResource;
import org.springframework.data.mongodb.gridfs.GridFsTemplate;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
import org.springframework.web.client.RestTemplate;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

/**
 * @author 余小小
 * @data 2022/3/14
 */

@Service
public class CmsPageService {

    @Autowired
    CmsPageRepository cmsPageRepository;

    //    cms页面配置
    @Autowired
    CmsConfigRepository cmsConfigRepository;

    //    cms模板接口
    @Autowired
    CmsTemplateRepository cmsTemplateRepository;

    // springframework.data.mongodb.gridfs.
    @Autowired
    public GridFsTemplate gridFsTemplate;

    //自定义的 mongoConfig
    @Autowired
    MongoConfig mongoConfig;

    //在 mongoConfig中注入的
    @Autowired
    GridFSBucket gridFSBucket;

    // org.springframework.web.client
    @Autowired
    RestTemplate restTemplate;

    //RMQ消息
    @Autowired
    RabbitTemplate rabbitTemplate;


    /**
     * 页面查询方法
     *
     * @param page             页码，从1开始记录
     * @param size             每一页的记录数
     * @param queryPageRequest 查询条件
     * @return
     */
    public QueryResponseResult cmsPageFindList(int page, int size, QueryPageRequest queryPageRequest) {

        if (queryPageRequest == null) {
            new QueryPageRequest();
        }
        //        条件匹配器 对pageAliase字段模糊查询，包含了即可
        ExampleMatcher exampleMatcher = ExampleMatcher.matching()
                .withMatcher("pageAliase", ExampleMatcher.GenericPropertyMatchers.contains());

//        定义条件值对象
        CmsPage cmsPage = new CmsPage();

//        判断前台传过来的条件
//        站点id
        if (StringUtils.isNotEmpty(queryPageRequest.getSiteId())) {
            cmsPage.setSiteId(queryPageRequest.getSiteId());
        }
//        模板id
        if (StringUtils.isNotEmpty(queryPageRequest.getTemplateId())) {
            cmsPage.setTemplateId(queryPageRequest.getTemplateId());
        }
//        页面别名
        if (StringUtils.isNotEmpty(queryPageRequest.getPageAliase())) {
            cmsPage.setPageAliase(queryPageRequest.getPageAliase());
        }

//        定义条件对象
        Example<CmsPage> example = Example.of(cmsPage, exampleMatcher);


//        page和size的默认值
        if (page <= 0) {
            //前端显示第一页是1
            page = 1;
        }
        int realPage = page - 1;//真实用来计算的是从0开始（第一页）
        if (size <= 0) {
            size = 10;
        }
//        使用分页条件查询插件
        Pageable pageable = PageRequest.of(realPage, size);
//        条件和分页查询
        Page<CmsPage> all = cmsPageRepository.findAll(example, pageable);
//        返回结果对象封装
        QueryResult queryResult = new QueryResult();
        queryResult.setList(all.getContent()); //数据的内容
        queryResult.setTotal(all.getTotalElements());//数据总记录数
        return new QueryResponseResult(CommonCode.SUCCESS, queryResult);
    }

    /**
     * 新增页面信息
     * 前端非空判断拦截
     * 判断索引唯一性：页面名称+站点id+页面webpath
     *
     * @param cmsPage 页面信息对象
     * @return
     */
    public CmsPageResult cmsPageAdd(CmsPage cmsPage) {

        if (cmsPage == null) {
//            抛出统一异常，非法对象
            ExceptionCast.cast(CommonCode.FAIL);
        }
//        判断页面是否存在
        CmsPage byPageNameAndSiteIdAndPageWebPath = cmsPageRepository.findByPageNameAndSiteIdAndPageWebPath(cmsPage.getPageName(), cmsPage.getSiteId(), cmsPage.getPageWebPath());
        if (byPageNameAndSiteIdAndPageWebPath != null) {
//            抛出自定义异常信息
            ExceptionCast.cast(CmsCode.CMS_ADDPAGE_EXISTSNAME);
        }
        cmsPage.setPageId(null);//让mongo一定自动生成id字段信息
        cmsPageRepository.save(cmsPage);
        return new CmsPageResult(CommonCode.SUCCESS, cmsPage);

    }
/*
    public CmsPageResult cmsPageAdd(CmsPage cmsPage) {
//        判断页面是否存在
        CmsPage byPageNameAndSiteIdAndPageWebPath = cmsPageRepository.findByPageNameAndSiteIdAndPageWebPath(cmsPage.getPageName(), cmsPage.getSiteId(), cmsPage.getPageWebPath());
        if (byPageNameAndSiteIdAndPageWebPath == null) {
            cmsPage.setPageId(null);//让mongo一定自动生成id字段信息
            cmsPageRepository.save(cmsPage);
            return new CmsPageResult(CommonCode.SUCCESS, cmsPage);
        }
        return new CmsPageResult(CommonCode.FAIL, null);

    }
*/


    /**
     * 通过id查询页面信息
     *
     * @param pageId
     * @return
     */
    public CmsPage findCmsPageById(String pageId) {
        Optional<CmsPage> optional = cmsPageRepository.findById(pageId);
        if (optional.isPresent()) {
            CmsPage cmsPage = optional.get();
            return cmsPage;
        }
        return null;
    }

    /**
     * 修改页面信息
     *
     * @param pageId
     * @param cmsPage
     * @return
     */
    public CmsPageResult editPage(String pageId, CmsPage cmsPage) {
//        通过id查询页面信息
        CmsPage cmsPageById = this.findCmsPageById(pageId);
        if (cmsPageById != null) {
//            设置要修改的对象
            cmsPageById.setTemplateId(cmsPage.getTemplateId()); //更新所属站点

            cmsPageById.setSiteId(cmsPage.getSiteId()); //更新页面别名

            cmsPageById.setPageAliase(cmsPage.getPageAliase()); //更新页面名称

            cmsPageById.setPageName(cmsPage.getPageName()); //更新访问路径

            cmsPageById.setPageWebPath(cmsPage.getPageWebPath()); //更新物理路径

            cmsPageById.setPagePhysicalPath(cmsPage.getPagePhysicalPath()); //执行更新

            cmsPageById.setDataUrl(cmsPage.getDataUrl());//数据Url

            cmsPageById.setPageType(cmsPage.getPageType());//型（静态/动态）

            cmsPageById.setPageCreateTime(cmsPage.getPageCreateTime());//创建时间
            cmsPageRepository.save(cmsPageById);//执行修改
            return new CmsPageResult(CommonCode.SUCCESS, cmsPageById);
        }
        return new CmsPageResult(CommonCode.FAIL, null);

    }


    /**
     * 删除页面
     *
     * @param pageId 页面的id
     * @return
     */
    public ResponseResult delPage(String pageId) {
        //        通过id查询页面信息
        CmsPage cmsPageById = this.findCmsPageById(pageId);
        if (cmsPageById != null) {
            cmsPageRepository.deleteById(pageId);
            return new ResponseResult(CommonCode.SUCCESS);
        }
        return new ResponseResult(CommonCode.FAIL);

    }


    /**
     * 页面静态化
     *
     * @param pageId
     * @return 1、填写页面DataUrl在编辑cms页面信息界面填写DataUrl，将此字段保存到cms_page集合中。
     * 2、静态化程序获取页面的DataUrl
     * 3、静态化程序远程请求DataUrl获取数据模型。
     * 4、静态化程序获取页面的模板信息
     * 5、执行页面静态化
     */
    public String getPageHtml(String pageId) {
//        获取数据模型
        Map model = getModelByPageId(pageId);
        if (model == null) {
//            模型获取不到
            ExceptionCast.cast(CmsCode.CMS_GENERATEHTML_DATAISNULL);
        }


//        获取页面的模板信息
        String templateContent = getTemplateByPageId(pageId);
        if (StringUtils.isEmpty(templateContent)) {
            ExceptionCast.cast(CmsCode.CMS_GENERATEHTML_TEMPLATEISNULL);
        }

//        执行静态化
        String html = doGeneralteHtml(templateContent, model);

        return html;
    }


    /**
     * 页面静态化
     * 模板+数据=页面
     */
    private String doGeneralteHtml(String templateContent, Map model) {
//        创建配置对象 Configuration:package freemarker.template;
        Configuration configuration = new Configuration(Configuration.getVersion());
//        创建模板加载器
        StringTemplateLoader stringTemplateLoader = new StringTemplateLoader();
        stringTemplateLoader.putTemplate("template", templateContent);

//        在Configuration配置模板加载器
        configuration.setTemplateLoader(stringTemplateLoader);
//        获取模板
        try {
            Template template = configuration.getTemplate("template");
//            调用api，执行静态化
            String content = FreeMarkerTemplateUtils.processTemplateIntoString(template, model);
            return content;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;

    }


    /**
     * 获取页面模板信息
     *
     * @param pageId
     * @return
     */
    private String getTemplateByPageId(String pageId) {
//        获取页面信息
        CmsPage cmsPageById = findCmsPageById(pageId);

        if (cmsPageById == null) {
//            静态页面不存在
            ExceptionCast.cast(CmsCode.CMS_NoATEHTML_SAVEHTMLERROR);
        }

//        获取页面模板id
        String templateId = cmsPageById.getTemplateId();
        if (StringUtils.isEmpty(templateId)) {
            ExceptionCast.cast(CmsCode.CMS_GENERATEHTML_TEMPLATEISNULL);
        }


//        查询模板信息
        Optional<CmsTemplate> optional = cmsTemplateRepository.findById(templateId);
        if (optional.isPresent()) {
            CmsTemplate cmsTemplate = optional.get();
//            获取模板文件id
            String templateFileId = cmsTemplate.getTemplateFileId();


//            从GridFs中获取模板文件
            //        通过文件的id查询文件 通过数据库中的_id字段
            GridFSFile gridFSFile = gridFsTemplate.findOne(Query.query(Criteria.where("_id").is(templateFileId)));

//        打开下载刘
            GridFSDownloadStream gridFSDownloadStream = gridFSBucket.openDownloadStream(gridFSFile.getObjectId());

//        创建GridFsResource对象，获取流
            GridFsResource gridFsResource = new GridFsResource(gridFSFile, gridFSDownloadStream);

//        从流中取数据 import org.apache.commons.io.IOUtils;
            String content = null;
            try {
                content = IOUtils.toString(gridFsResource.getInputStream(), "utf-8");
                return content;
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;

    }


    //    获取数据模型
    private Map getModelByPageId(String pageId) {
//        获取页面信息
        CmsPage cmsPageById = findCmsPageById(pageId);
        if (cmsPageById == null) {
            ExceptionCast.cast(CmsCode.CMS_NoATEHTML_SAVEHTMLERROR);
        }

//        获取页面的dataUrl
        String dataUrl = cmsPageById.getDataUrl();
        if (StringUtils.isEmpty(dataUrl)) {
//            页面的dataUrl为空
            ExceptionCast.cast(CmsCode.CMS_GENERATEHTML_DATAISNULL);
        }


//        通过restTemplate请求dataUrl获取数据

        ResponseEntity<Map> forEntity = restTemplate.getForEntity(dataUrl, Map.class);
        Map body = forEntity.getBody();
        return body;
    }


    /**
     * 通过cms的id查询cms的配置对象 cmsconfig
     * 根据id查询配置管理信息
     * findById 是springdata（mongo）自带的
     */
    public CmsConfig getConfigById(String id) {
        Optional<CmsConfig> optional = cmsConfigRepository.findById(id);
        if (optional.isPresent()) {
            CmsConfig cmsConfig = optional.get();
            return cmsConfig;
        }
        return null;
    }


    /**
     * 发布页面
     *
     * @param pageId 页面的id
     * @return 执行页面静态化
     * 将页面静态化文件持久化存储GFS中
     * 向MQ发送消息，让CMS客户端了解可以下载咯
     */

    public ResponseResult postPage(String pageId) {
//        执行页面静态化
        String pageHtml = this.getPageHtml(pageId);
//        将页面静态化文件持久化存储GFS中
        CmsPage cmsPage = saveHtml(pageId, pageHtml);
//        向MQ发送消息
        this.sendPostPage(pageId);
        return new ResponseResult(CommonCode.SUCCESS);

    }


    /**
     * 保存html到GFS
     * 将html文件内容保存GFS
     * 将html文件id更新到cmspge对象中
     */
    private CmsPage saveHtml(String pageId, String htmlContent) {
//        获取页面的信息
        CmsPage cmsPage = this.findCmsPageById(pageId);
        if (cmsPage == null) {
//            页面的dataUrl为空
            ExceptionCast.cast(CommonCode.INVALID_PARAM);
        }

        //存储之前先删除旧的html
        String htmlFileId = cmsPage.getHtmlFileId();
        if (org.apache.commons.lang.StringUtils.isNotEmpty(htmlFileId)) {
            gridFsTemplate.delete(Query.query(Criteria.where("_id").is(htmlFileId)));
        }


        //保存html文件到GridFS
        InputStream inputStream = null;
        try {
            inputStream = IOUtils.toInputStream(htmlContent, "utf-8");
        } catch (IOException e) {
            e.printStackTrace();
        }
        ObjectId objectId = gridFsTemplate.store(inputStream, cmsPage.getPageName());
        //文件id
        String fileId = objectId.toString();
        //将文件id存储到cmspage中
        cmsPage.setHtmlFileId(fileId);
        cmsPageRepository.save(cmsPage);
        return cmsPage;
    }


    /**
     * 给mq发送消息
     *
     * @param pageId
     */
    public void sendPostPage(String pageId) {
//        获取页面信息
        CmsPage cmsPage = this.findCmsPageById(pageId);
        if (cmsPage == null) {
//            页面的dataUrl为空
            ExceptionCast.cast(CommonCode.INVALID_PARAM);
        }


//        消息对象
        Map<String, String> msg = new HashMap<>();
        msg.put("pageId", pageId); //json中属性值
        //转成json字符串
        String jsonString = JSON.toJSONString(msg);
//        发送给mq
//        获取站点id
        String siteId = cmsPage.getSiteId();

        this.rabbitTemplate.convertAndSend(RabbitmqConfig.EX_ROUTING_CMS_POSTPAGE, siteId, jsonString);

    }


}
